package com.lagou.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName IPFilter
 * @Description IP防爆刷过滤器, 先进行这个过滤器再进行token校验
 * @Author playboy
 * @Date 2021/6/26 3:35 下午terger
 * @Version 1.0
 **/
@Component
@RefreshScope
public class IPFilter implements GlobalFilter, Ordered, InitializingBean {
	private static Logger log = LoggerFactory.getLogger(IPFilter.class);

	@Value("${ip.minute}")
	private Integer minute;
	@Value("${ip.maxcount}")
	private Integer maxcount;

	private static Map<String, AtomicInteger> countsMap = new ConcurrentHashMap<>();

	private static Map<String, Queue<AtomicInteger>> manyMin = new ConcurrentHashMap<>();

	private static Map<String, AtomicInteger> oneMin = new ConcurrentHashMap<>();

	static private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);


	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpResponse response = exchange.getResponse();
		String hostAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
		AtomicInteger counts = countsMap.get(hostAddress);
		if (ObjectUtils.isEmpty(counts)) {
			counts = new AtomicInteger(0);
			countsMap.put(hostAddress, counts);
			oneMin.put(hostAddress, new AtomicInteger(0));
		}
		AtomicInteger one = oneMin.get(hostAddress);
		one.getAndIncrement();
		int result = counts.incrementAndGet();
		if (result > maxcount.intValue()) {
			DataBuffer wrap =
					response.bufferFactory().wrap("请求次数过多".getBytes(StandardCharsets.UTF_8));
			response.setStatusCode(HttpStatus.FORBIDDEN);
			return response.writeWith(Mono.just(wrap));
		}

		return chain.filter(exchange);
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		scheduledThreadPoolExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				for (Map.Entry<String, AtomicInteger> entry : countsMap.entrySet()) {
					String key = entry.getKey();
					Queue<AtomicInteger> queue = manyMin.get(key);
					if (ObjectUtils.isEmpty(queue)) {
						queue = new ArrayBlockingQueue<AtomicInteger>(minute);
						manyMin.put(key, queue);
					}
					//判断queue是否满了
					if (queue.size() >= minute) {
						//移除一分钟的数据
						System.out.println("移除一分钟的数据");
						AtomicInteger poll = queue.poll();
						int val = 0 - poll.intValue();
						entry.getValue().addAndGet(val);
					}
					//将最远的一分钟数据清除
					AtomicInteger atomicInteger = oneMin.get(entry.getKey());
					queue.offer(atomicInteger);
					oneMin.put(entry.getKey(), new AtomicInteger(0));
				}
			}
		}, 1L, 1L, TimeUnit.MINUTES);
	}
}
